/*
 * OPIAM Suite
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package opiam.admin.faare.persistence;

import opiam.admin.faare.service.UserContext;

import org.apache.log4j.Logger;

import java.lang.reflect.Array;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;


/**
 * Lazy loading for string attributes.
 */
public class LazyStringCollection extends LazyCollection implements Collection
{
    /** Log4J. */
    private static Logger _logger = Logger.getLogger(LazyStringCollection.class.getName());

    /** Dname of entry containing attribute to load. */
    private String dn;

    /** List of loaded values. */
    private ArrayList values;

    /** List of deleted elements. */
    private ArrayList deleted;

    /** List of added elements. */
    private ArrayList added;

    /** The change count of the collection. */
    private int changecount;

    /** The number of elements in this collection. */
    private int size;

    /** The "loaded" flag. */
    private boolean load = false;

    /** Attribute to load. */
    private String att;

    /** Connected user context. */
    private UserContext userContext;

    /**
     * Creates a new LazyStringCollection object.
     *
     * @param enclosing Dname of entry containing attribute to load.
     * @param atts Attribute to load.
     * @param auserContext Connected user context.
     */
    public LazyStringCollection(String enclosing, ArrayList atts,
        UserContext auserContext)
    {
        super(enclosing, atts, auserContext);
        this.dn = enclosing;
        this.att = (String) atts.get(0);
        this.deleted = new ArrayList();
        this.added = new ArrayList();
        this.userContext = auserContext;
    }

    /**
     * @see java.util.Collection#add(Object).
     */
    @Override
	public boolean add(Object o)
    {
        if (!load)
        {
            lazyLoad();
        }

        String s = (String) o;

        if (values.contains(s))
        {
            if (deleted.contains(s))
            {
                deleted.remove(s);
                changecount++;

                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            if (deleted.contains(s))
            {
                throw new RuntimeException("Illegal Internal State.");
            }

            if (added.add(s))
            {
                changecount++;
                size++;

                return true;
            }
            else
            {
                return false;
            }
        }
    }

    /**
     * @see java.util.Collection#addAll(Collection).
     */
    @Override
	public boolean addAll(Collection c)
    {
        if (!load)
        {
            lazyLoad();
        }

        boolean changed = false;
        Iterator a = c.iterator();

        while (a.hasNext())
        {
            if (add(a.next()))
            {
                changed = true;
            }
        }

        if (changed)
        {
            changecount++;
        }

        return changed;
    }

    /**
     * @see java.util.Collection#clear().
     */
    @Override
	public void clear()
    {
        if (!load)
        {
            lazyLoad();
        }

        Iterator itor = iterator();

        while (itor.hasNext())
        {
            itor.next();
            itor.remove();
        }
    }

    /**
     * @see java.util.Collection#contains(Object).
     */
    @Override
	public boolean contains(Object o)
    {
        if (!load)
        {
            lazyLoad();
        }

        if (added.contains(o))
        {
            return true;
        }

        if (values.contains(o) && !deleted.contains(o))
        {
            return true;
        }

        return false;
    }

    /**
     * @see java.util.Collection#containsAll(Collection).
     */
    @Override
	public boolean containsAll(Collection c)
    {
        if (!load)
        {
            lazyLoad();
        }

        Iterator it = c.iterator();

        while (it.hasNext())
        {
            if (!contains(it.next()))
            {
                return false;
            }
        }

        return true;
    }

    /**
     * @see java.lang.Object#equals(Object).
     */
    @Override
	public boolean equals(Object o)
    {
        if (!load)
        {
            lazyLoad();
        }

        return this == o;
    }

    /**
     * @see java.util.Collection#isEmpty().
     */
    @Override
	public boolean isEmpty()
    {
        if (!load)
        {
            lazyLoad();
        }

        return size() == 0;
    }

    /**
     * @see java.util.Collection#iterator().
     */
    @Override
	public Iterator iterator()
    {
        return new LazyIterator(this);
    }

    /**
     * @see java.util.Collection#remove(Object).
     */
    @Override
	public boolean remove(Object o)
    {
        if (!load)
        {
            lazyLoad();
        }

        String s = (String) o;
        _logger.debug("remove s : " + s);

        boolean changed = false;

        if (deleted.contains(s))
        {
            return false;
        }

        if (added.contains(s))
        {
            added.remove(s);
            changecount++;
            size--;

            return true;
        }
        else if (values.contains(s))
        {
            deleted.add(s);
            changecount++;
            size--;

            return true;
        }

        return false;
    }

    /**
     * @see java.util.Collection#removeAll(Collection).
     */
    @Override
	public boolean removeAll(Collection c)
    {
        if (!load)
        {
            lazyLoad();
        }

        Object o;
        boolean changed = false;
        Iterator it = c.iterator();

        while (it.hasNext())
        {
            if (remove(it.next()))
            {
                changed = true;
            }
        }

        if (changed)
        {
            changecount++;
        }

        return changed;
    }

    /**
     * @see java.util.Collection#retainAll(Collection).
     */
    @Override
	public boolean retainAll(Collection c)
    {
        if (!load)
        {
            lazyLoad();
        }

        Object o;
        boolean changed = false;
        Iterator org = iterator();

        while (org.hasNext())
        {
            o = org.next();

            if (!c.contains(o))
            {
                changed = true;
                org.remove();
            }
        }

        if (changed)
        {
            changecount++;
        }

        return changed;
    }

    /**
     * @see java.util.Collection#size().
     */
    @Override
	public int size()
    {
        if (!load)
        {
            lazyLoad();
        }

        return size;
    }

    /**
     * @see java.util.Collection#toArray().
     */
    @Override
	public Object[] toArray()
    {
        if (!load)
        {
            lazyLoad();
        }

        Object[] result = new Object[size()];
        Iterator itor = iterator();

        //DW/2535/BeginPatch
        int count = 0;

        //DW/2535/EndPatch
        while (itor.hasNext())
        {
            //DW/2535/BeginPatch
            result[count] = itor.next();
            count++;

            //DW/2535/EndPatch
        }

        return result;
    }

    /**
     * @see java.util.Collection#toArray(Object[]).
     */
    @Override
	public Object[] toArray(Object[] a)
    {
        if (!load)
        {
            lazyLoad();
        }

        if (a == null)
        {
            throw new NullPointerException();
        }

        Object[] result;
        int asize = size();

        if (asize < a.length)
        {
            result = a;
        }
        else
        {
            result = (Object[]) Array.newInstance(a.getClass().getComponentType(),
                    asize);
        }

        Iterator itor = iterator();
        int count = 0;

        while (itor.hasNext())
        {
            result[count++] = itor.next();
        }

        // patch the extra space with null
        while (count < result.length)
        {
            result[count++] = null;
        }

        return result;
    }

    /**
     * Loads attribute values.
     * @return List of values.
     */
    @Override
	public ArrayList getIdentitiesList()
    {
        if (!load)
        {
            lazyLoad();
        }

        ArrayList result = new ArrayList();
        result.addAll(values);
        result.addAll(added);
        result.removeAll(deleted);

        return result;
    }

    /**
     * Gets deleted values.
     * @return List of deleted values.
     */
    public ArrayList getDeleted()
    {
        if (!load)
        {
            lazyLoad();
        }

        return (ArrayList) deleted.clone();
    }

    /**
     * Gets added values.
     * @return Added values.
     */
    public ArrayList getAdded()
    {
        if (!load)
        {
            lazyLoad();
        }

        return (ArrayList) added.clone();
    }

    /**
     * Loads values.
     */
    protected void lazyLoad()
    {
        try
        {
            values = (ArrayList) PersistenceLDAP.loadAttribute(dn, att,
                    userContext);

            load = true;
            size = values.size();
        }
        catch (Exception e)
        {
            if (_logger.isDebugEnabled())
            {
                _logger.debug("Trace", e);
            }

            throw new RuntimeException(
                "PersistenceException for lazy loaded object\n" + e);
        }
    }

    /**
     * Gets change count of the collection.
     * @return Change count.
     */
    protected int getChangecount()
    {
       return changecount;
    }

    /**
     * Increments change count of the collection.
     */
    protected void incChangecount()
    {
       changecount++;
    }

    /**
     * Decrements number of elements.
     */
    protected void decSize()
    {
        size--;
    }

    /**
     * Gets dname of entry containing attribute to load.
     * @return dname
     */
    protected String getDn()
    {
       return dn;
    }

    /**
     * Gets attribute to load.
     * @return attribute name
     */
    protected String getAtt()
    {
        return att;
    }

    /**
     * Gets list of loaded values.
     * @return values
     */
    protected ArrayList getValues()
    {
        return values;
    }

    /**
     * Sets list of loaded values.
     * @param v values
     */
    protected void setValues(ArrayList v)
    {
        values = v;
        size = values.size();
    }

    /**
     * Gets the "loaded" flag.
     * @return flag
     */
    protected boolean isLoaded()
    {
        return load;
    }

    /**
     * Sets the "loaded" flag.
     * @param l flag
     */
    protected void setLoaded(boolean l)
    {
        load = l;
    }

    /**
     * Gets connected user context.
     * @return user context
     */
    protected UserContext getUserContext()
    {
        return userContext;
    }
}
